namespace ScrumSprintMonitor.Modules.WorkItemMonitor.ViewModels
{
	#region #using Directives

	using System;
	using System.Collections.Generic;
	using System.Diagnostics;
	using System.Linq;
	using System.Windows.Media;

	using Infrastructure;
	using Infrastructure.Configuration;

	using Microsoft.Practices.ServiceLocation;

	using UI.Wpf.ViewModels;

	#endregion

	public class SprintStatsViewModel : SprintViewModelBase,
	                                    ISprintStatsViewModel
	{
		static SprintStatsViewModel()
		{
			var dependsOn = Register<SprintStatsViewModel>();

			dependsOn.Call(me => me.OnPropertyChanged("AvailableHours")).OnChanged(me => me.CurrentSprint.AvailableTime);
			dependsOn.Call(me => me.OnPropertyChanged("TimeIncludesFreeBusyInformation")).OnChanged(me => me.CurrentSprint.TimeIncludesFreeBusyInformation);

			dependsOn.Call(me => me.OnPropertyChanged("DifferenceToSchedule")).OnChanged(me => me.AvailableHours).OnChanged(me => me.NotDoneWork_TotalHours).
				OnChanged(me => me.ActiveWork_TotalHoursRemaining);
			dependsOn.Call(me => me.OnPropertyChanged("IsAheadOfSchedule")).OnChanged(me => me.AvailableHours).OnChanged(me => me.NotDoneWork_TotalHours).
				OnChanged(me => me.ActiveWork_TotalHoursRemaining);
			dependsOn.Call(me => me.OnPropertyChanged("AheadOfScheduleForegroundBrush")).OnChanged(me => me.IsAheadOfSchedule);

			dependsOn.Call(me => me.OnPropertyChanged("AverageResolutionTime")).OnChanged(me => me.ReadyForTestWork_TotalHours).OnChanged(
				me => me.ReadyForTestWork_BacklogItemCount);
			dependsOn.Call(me => me.OnPropertyChanged("ShowAheadOfScheduleBy")).OnChanged(me => me.CurrentSprint.NotAllWorkItemsEstimated).OnChanged(
				me => me.AverageResolutionTime).OnChanged(me => me.shellLayoutConfiguration.ActiveSprintMode == SprintMode.Sprinting);
			dependsOn.Call(me => me.OnPropertyChanged("ShowAverageResolutionTime")).OnChanged(me => me.ReadyForTestWork_BacklogItemCount).OnChanged(
				me => me.shellLayoutConfiguration.ActiveSprintMode);
		}

		public SprintStatsViewModel(IServiceLocator serviceLocator, IShellLayoutConfiguration shellLayoutConfiguration)
			: base(serviceLocator, true)
		{
			this.shellLayoutConfiguration = shellLayoutConfiguration;
		}

		#region Private Fields

		private readonly IShellLayoutConfiguration shellLayoutConfiguration;
		private double activeWork_TotalHoursRemaining;
		private double notDoneWork_TotalHours;
		private int readyForTestWork_BacklogItemCount;
		private double readyForTestWork_TotalHours;

		#endregion

		#region Implementation of ISprintStatsViewModel

		public double AvailableHours
		{
			[DebuggerStepThrough]
			get { return CurrentSprint.AvailableTime.TotalHours; }
		}

		public bool TimeIncludesFreeBusyInformation
		{
			[DebuggerStepThrough]
			get
			{
				GuardDisposed();
				return (CurrentSprint != null ? CurrentSprint.TimeIncludesFreeBusyInformation : false);
			}
		}

		public double AverageResolutionTime
		{
			[DebuggerStepThrough]
			get { return ReadyForTestWork_TotalHours / ReadyForTestWork_BacklogItemCount; }
		}

		public bool ShowAverageResolutionTime
		{
			get
			{
				EnsureConnected(true);
				return ReadyForTestWork_BacklogItemCount > 0.0 && this.shellLayoutConfiguration.ActiveSprintMode == SprintMode.Sprinting;
			}
		}

		public bool IsAheadOfSchedule
		{
			get
			{
				EnsureConnected(true);
				return AvailableHours >= (NotDoneWork_TotalHours + ActiveWork_TotalHoursRemaining);
			}
		}

		public double DifferenceToSchedule
		{
			get
			{
				EnsureConnected(true);
				return Math.Abs(AvailableHours - (NotDoneWork_TotalHours + ActiveWork_TotalHoursRemaining));
			}
		}

		public Brush AheadOfScheduleForegroundBrush
		{
			get { return (IsAheadOfSchedule ? Brushes.White : Brushes.Red); }
		}

		public bool ShowAheadOfScheduleBy
		{
			get
			{
				EnsureConnected(true);
				return !CurrentSprint.NotAllWorkItemsEstimated &&
				       (!double.IsNaN(AverageResolutionTime) || this.shellLayoutConfiguration.ActiveSprintMode == SprintMode.Sprinting);
			}
		}

		#endregion

		#region Private Methods

		private double ActiveWork_TotalHoursRemaining
		{
			get
			{
				GuardDisposed();
				EnsureConnected(true);
				return this.activeWork_TotalHoursRemaining;
			}
			set
			{
				GuardDisposed();
				if (value != this.activeWork_TotalHoursRemaining)
				{
					this.activeWork_TotalHoursRemaining = value;
					OnPropertyChanged("ActiveWork_TotalHoursRemaining");
				}
			}
		}

		private double NotDoneWork_TotalHours
		{
			get
			{
				GuardDisposed();
				EnsureConnected(true);
				return this.notDoneWork_TotalHours;
			}
			set
			{
				GuardDisposed();
				if (value != this.notDoneWork_TotalHours)
				{
					this.notDoneWork_TotalHours = value;
					OnPropertyChanged("NotDoneWork_TotalHours");
				}
			}
		}

		private double ReadyForTestWork_TotalHours
		{
			get
			{
				GuardDisposed();
				EnsureConnected(true);
				return this.readyForTestWork_TotalHours;
			}
			set
			{
				GuardDisposed();
				if (value != this.readyForTestWork_TotalHours)
				{
					this.readyForTestWork_TotalHours = value;
					OnPropertyChanged("ReadyForTestWork_TotalHours");
				}
			}
		}

		private int ReadyForTestWork_BacklogItemCount
		{
			get
			{
				GuardDisposed();
				EnsureConnected(true);
				return this.readyForTestWork_BacklogItemCount;
			}
			set
			{
				GuardDisposed();
				if (value != this.readyForTestWork_BacklogItemCount)
				{
					this.readyForTestWork_BacklogItemCount = value;
					OnPropertyChanged("ReadyForTestWork_BacklogItemCount");
				}
			}
		}

		#endregion

		#region Overrides

		protected override void OnCalculateWorkItemData(IList<IWorkItemAdapter> workItems)
		{
			base.OnCalculateWorkItemData(workItems);

			ActiveWork_TotalHoursRemaining =
				workItems.Sum(x => x.Type == WorkItemType.SprintBacklogItem && x.State == WorkItemState.InProgress ? x.RemainingWork : 0.0);
			NotDoneWork_TotalHours = workItems.Sum(x => x.Type == WorkItemType.SprintBacklogItem && x.State == WorkItemState.NotDone ? x.RemainingWork : 0.0);
			ReadyForTestWork_TotalHours = workItems.Sum(x => x.Type == WorkItemType.SprintBacklogItem && x.State == WorkItemState.Done ? x.DoneWork : 0.0);
			ReadyForTestWork_BacklogItemCount = workItems.Count(x => x.Type == WorkItemType.SprintBacklogItem && x.State == WorkItemState.Done);
		}

		#endregion
	}
}